
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h> 　　
#include <stdlib.h>
#include <string.h>

#include "pager.h"

struct Payload {
	char *key;
	char *value;
};

// paged file manager
struct PageManager {
	int fd;
	char name[16]; // first page [4~20]
	uint page_count;
};

// one page
struct Page {
	pgno pn; 				// page number
	uint cell_num;			//
	uint first_cell_off;	//
};

void errExit(const char * errmsg) {
	perror(errmsg);
	exit(1);
}

void printPm(pPageMgr mgr) {
	printf("--------PageManager-------\n");
	printf("  name=%s\n", mgr->name);
	printf("  page_count=%d\n", mgr->page_count);
}

pPageMgr _createPm(const char* name) {
	printf("_createPm\n");
	mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
	int flags = O_RDWR | O_CREAT | O_TRUNC;
	int fd = open(name, flags, mode);
	if (fd == -1) {
		errExit("open fail");
	}

	// write the first page, set page_count = 0;
	char* buf = malloc(PAGE_SZ);
	*((uint*) buf) = 0; // page_count; big-endian ???
	strncpy(buf + NAME_START, DB_NAME, NAME_LEN); // cp db_name
	lseek(fd, 0, SEEK_SET);
	write(fd, buf, PAGE_SZ);
	free(buf);

	pPageMgr mgr = calloc(sizeof(struct PageManager), 1);
	mgr->fd = fd;
	mgr->page_count = 0;
	strncpy(mgr->name, DB_NAME, NAME_LEN);
	return mgr;
}

pPageMgr openPm(const char* name) {
	mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
	int flags = O_RDWR;
	int fd = open(name, flags, mode);
	if (fd == -1) {
		return _createPm(name);
	}

	pPageMgr mgr = calloc(sizeof(struct PageManager), 1);
	mgr->fd = fd;

	// read the first page
	char* buf = malloc(PAGE_SZ);
	lseek(fd, 0, SEEK_SET);
	read(fd, buf, PAGE_SZ);
	mgr->page_count = (uint*)buf[0];
	strncpy(mgr->name, buf + NAME_START, NAME_LEN);
	free(buf);
	return mgr;
}

void closePm(pPageMgr mgr) {
	// TODO release pages
	close(mgr->fd);
}

pPayload newInfo(const char* key, const char* value) {
	pPayload pload = malloc(sizeof(struct Payload));
	int nKey = strlen(key);
	pload->key = malloc(nKey);
	strncpy(pload->key, key, nKey);

	int nValue = strlen(value);
	pload->value = malloc(nValue);
	strncpy(pload->value, value, nValue);
	return pload;
}

void save(pPageMgr mgr, pPayload load) {
	int newPgno = ++ mgr->page_count ;
	char* buf = calloc(PAGE_SZ, 1);
	*((int*)buf) = newPgno;
	int nKey = strlen(load->key);
	int nValue = strlen(load->value);
	int nAll = nKey + nValue;
	strncpy(buf + PAGE_SZ - nAll, load->key, nKey);
	strncpy(buf + PAGE_SZ - nValue, load->value, nValue);
	int fd = mgr->fd;
	lseek(fd, PAGE_SZ * newPgno, SEEK_SET);
	write(fd, buf, PAGE_SZ);

	//
	lseek(fd, 0, SEEK_SET);
	write(fd, buf, PAGE_SZ);
}



/********test*******/
void main(void) {
	pPageMgr mgr = openPm("foo.db");
	printPm(mgr);
	pPayload pload = newInfo("hello", "world");
	save(mgr, pload);
	// release pload
	printPm(mgr);
}